#include "network_protocol.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Result Set Management Functions

mysql_result_set_t* mysql_result_set_create(uint64_t column_count) {
    if (column_count == 0 || column_count > 4096) return NULL;  // Reasonable limit
    
    mysql_result_set_t* result = malloc(sizeof(mysql_result_set_t));
    if (!result) return NULL;
    
    memset(result, 0, sizeof(mysql_result_set_t));
    
    result->column_count = column_count;
    result->fields = malloc(column_count * sizeof(mysql_field_def_t));
    if (!result->fields) {
        free(result);
        return NULL;
    }
    
    // Initialize field definitions
    for (uint64_t i = 0; i < column_count; i++) {
        memset(&result->fields[i], 0, sizeof(mysql_field_def_t));
    }
    
    return result;
}

void mysql_result_set_destroy(mysql_result_set_t* result) {
    if (!result) return;
    
    // Clean up field definitions
    if (result->fields) {
        for (uint64_t i = 0; i < result->column_count; i++) {
            mysql_field_def_t* field = &result->fields[i];
            if (field->catalog) free(field->catalog);
            if (field->schema) free(field->schema);
            if (field->table) free(field->table);
            if (field->org_table) free(field->org_table);
            if (field->name) free(field->name);
            if (field->org_name) free(field->org_name);
        }
        free(result->fields);
    }
    
    // Clean up rows
    if (result->rows) {
        for (uint64_t i = 0; i < result->row_count; i++) {
            if (result->rows[i]) {
                for (uint64_t j = 0; j < result->column_count; j++) {
                    if (result->rows[i][j]) {
                        free(result->rows[i][j]);
                    }
                }
                free(result->rows[i]);
            }
        }
        free(result->rows);
    }
    
    if (result->row_lengths) {
        free(result->row_lengths);
    }
    
    free(result);
}

int mysql_result_set_add_field(mysql_result_set_t* result, uint64_t index, 
                              const char* name, mysql_field_type_t type, 
                              uint32_t length) {
    if (!result || !name || index >= result->column_count) return -1;
    
    mysql_field_def_t* field = &result->fields[index];
    
    // Set default values
    field->catalog = strdup("def");
    field->schema = strdup("");
    field->table = strdup("");
    field->org_table = strdup("");
    field->name = strdup(name);
    field->org_name = strdup(name);
    field->length_of_fixed_fields = 12;  // Standard fixed fields length
    field->character_set = 0x21;  // utf8_general_ci
    field->column_length = length;
    field->type = type;
    field->flags = 0;
    field->decimals = 0;
    field->filler = 0;
    
    // Set field flags based on type
    switch (type) {
        case MYSQL_TYPE_TINY:
        case MYSQL_TYPE_SHORT:
        case MYSQL_TYPE_LONG:
        case MYSQL_TYPE_LONGLONG:
        case MYSQL_TYPE_INT24:
            field->flags |= 0x0020;  // NUM_FLAG
            break;
        case MYSQL_TYPE_FLOAT:
        case MYSQL_TYPE_DOUBLE:
        case MYSQL_TYPE_DECIMAL:
        case MYSQL_TYPE_NEWDECIMAL:
            field->flags |= 0x0020;  // NUM_FLAG
            break;
        case MYSQL_TYPE_VARCHAR:
        case MYSQL_TYPE_VAR_STRING:
        case MYSQL_TYPE_STRING:
            field->character_set = 0x21;  // utf8_general_ci
            break;
        default:
            break;
    }
    
    return 0;
}

int mysql_result_set_add_row(mysql_result_set_t* result, char** row_data, uint64_t* lengths) {
    if (!result || !row_data) return -1;
    
    // Expand rows array if needed
    char*** new_rows = realloc(result->rows, (result->row_count + 1) * sizeof(char**));
    if (!new_rows) return -1;
    result->rows = new_rows;
    
    uint64_t* new_row_lengths = realloc(result->row_lengths, 
                                       (result->row_count + 1) * sizeof(uint64_t));
    if (!new_row_lengths) return -1;
    result->row_lengths = new_row_lengths;
    
    // Allocate new row
    char** row = malloc(result->column_count * sizeof(char*));
    if (!row) return -1;
    
    // Copy column data
    for (uint64_t i = 0; i < result->column_count; i++) {
        if (row_data[i]) {
            uint64_t len = lengths ? lengths[i] : strlen(row_data[i]);
            row[i] = malloc(len + 1);
            if (!row[i]) {
                // Clean up on error
                for (uint64_t j = 0; j < i; j++) {
                    free(row[j]);
                }
                free(row);
                return -1;
            }
            memcpy(row[i], row_data[i], len);
            row[i][len] = '\0';
            result->row_lengths[result->row_count] += len;
        } else {
            row[i] = NULL;  // NULL value
        }
    }
    
    result->rows[result->row_count] = row;
    result->row_count++;
    
    return 0;
}

int mysql_send_result_set(mysql_connection_t* conn, mysql_result_set_t* result) {
    if (!conn || !result) return -1;
    
    conn->state = CONN_STATE_RESULT_SENDING;
    
    // Send column count
    size_t packet_size = mysql_lenenc_int_size(result->column_count);
    mysql_packet_t* packet = mysql_packet_create(packet_size, conn->sequence_id);
    if (!packet) return -1;
    
    mysql_packet_write_lenenc_int(packet, 0, result->column_count);
    
    if (mysql_send_packet(conn, packet) != 0) {
        mysql_packet_destroy(packet);
        return -1;
    }
    mysql_packet_destroy(packet);
    
    // Send field definitions
    if (mysql_send_field_definitions(conn, result) != 0) {
        return -1;
    }
    
    // Send EOF packet after field definitions (if not deprecate_eof)
    if (!(conn->capability_flags & CLIENT_DEPRECATE_EOF)) {
        if (mysql_send_eof(conn, 0, conn->status_flags) != 0) {
            return -1;
        }
    }
    
    // Send result rows
    if (mysql_send_result_rows(conn, result) != 0) {
        return -1;
    }
    
    // Send final EOF packet
    if (!(conn->capability_flags & CLIENT_DEPRECATE_EOF)) {
        if (mysql_send_eof(conn, 0, conn->status_flags) != 0) {
            return -1;
        }
    } else {
        // Send OK packet with EOF flag
        if (mysql_send_ok(conn, result->row_count, 0) != 0) {
            return -1;
        }
    }
    
    conn->state = CONN_STATE_COMMAND_READY;
    return 0;
}

int mysql_send_field_definitions(mysql_connection_t* conn, mysql_result_set_t* result) {
    if (!conn || !result) return -1;
    
    for (uint64_t i = 0; i < result->column_count; i++) {
        mysql_field_def_t* field = &result->fields[i];
        
        // Calculate packet size
        size_t packet_size = 
            mysql_lenenc_string_size(field->catalog ? field->catalog : "def") +
            mysql_lenenc_string_size(field->schema ? field->schema : "") +
            mysql_lenenc_string_size(field->table ? field->table : "") +
            mysql_lenenc_string_size(field->org_table ? field->org_table : "") +
            mysql_lenenc_string_size(field->name ? field->name : "") +
            mysql_lenenc_string_size(field->org_name ? field->org_name : "") +
            mysql_lenenc_int_size(field->length_of_fixed_fields) +
            2 + 4 + 1 + 2 + 1 + 2;  // Fixed fields
        
        mysql_packet_t* packet = mysql_packet_create(packet_size, conn->sequence_id);
        if (!packet) return -1;
        
        size_t offset = 0;
        
        // Catalog
        offset += mysql_packet_write_lenenc_string(packet, offset, 
                                                  field->catalog ? field->catalog : "def");
        
        // Schema
        offset += mysql_packet_write_lenenc_string(packet, offset, 
                                                  field->schema ? field->schema : "");
        
        // Table
        offset += mysql_packet_write_lenenc_string(packet, offset, 
                                                  field->table ? field->table : "");
        
        // Org table
        offset += mysql_packet_write_lenenc_string(packet, offset, 
                                                  field->org_table ? field->org_table : "");
        
        // Name
        offset += mysql_packet_write_lenenc_string(packet, offset, 
                                                  field->name ? field->name : "");
        
        // Org name
        offset += mysql_packet_write_lenenc_string(packet, offset, 
                                                  field->org_name ? field->org_name : "");
        
        // Length of fixed fields
        offset += mysql_packet_write_lenenc_int(packet, offset, field->length_of_fixed_fields);
        
        // Character set
        mysql_packet_write_uint16(packet, offset, field->character_set);
        offset += 2;
        
        // Column length
        mysql_packet_write_uint32(packet, offset, field->column_length);
        offset += 4;
        
        // Type
        mysql_packet_write_uint8(packet, offset, field->type);
        offset += 1;
        
        // Flags
        mysql_packet_write_uint16(packet, offset, field->flags);
        offset += 2;
        
        // Decimals
        mysql_packet_write_uint8(packet, offset, field->decimals);
        offset += 1;
        
        // Filler
        mysql_packet_write_uint16(packet, offset, field->filler);
        
        if (mysql_send_packet(conn, packet) != 0) {
            mysql_packet_destroy(packet);
            return -1;
        }
        
        mysql_packet_destroy(packet);
    }
    
    return 0;
}

int mysql_send_result_rows(mysql_connection_t* conn, mysql_result_set_t* result) {
    if (!conn || !result) return -1;
    
    for (uint64_t i = 0; i < result->row_count; i++) {
        char** row = result->rows[i];
        
        // Calculate packet size
        size_t packet_size = 0;
        for (uint64_t j = 0; j < result->column_count; j++) {
            if (row[j]) {
                packet_size += mysql_lenenc_string_size(row[j]);
            } else {
                packet_size += 1;  // NULL marker (0xfb)
            }
        }
        
        mysql_packet_t* packet = mysql_packet_create(packet_size, conn->sequence_id);
        if (!packet) return -1;
        
        size_t offset = 0;
        
        // Write column values
        for (uint64_t j = 0; j < result->column_count; j++) {
            if (row[j]) {
                offset += mysql_packet_write_lenenc_string(packet, offset, row[j]);
            } else {
                // NULL value
                mysql_packet_write_uint8(packet, offset, 0xfb);
                offset += 1;
            }
        }
        
        if (mysql_send_packet(conn, packet) != 0) {
            mysql_packet_destroy(packet);
            return -1;
        }
        
        mysql_packet_destroy(packet);
    }
    
    return 0;
}

// Utility functions for result set conversion

mysql_result_set_t* convert_gudb_result_to_mysql(result_t* gudb_result) {
    if (!gudb_result) return NULL;
    
    // This would convert GuDB's internal result format to MySQL format
    // For now, return NULL as this requires integration with GuDB's result structures
    return NULL;
}

int mysql_send_simple_ok(mysql_connection_t* conn, const char* message) {
    if (!conn) return -1;
    
    if (message) {
        size_t packet_size = 1 + mysql_lenenc_int_size(0) + mysql_lenenc_int_size(0) + 
                           2 + 2 + mysql_lenenc_string_size(message);
        
        mysql_packet_t* packet = mysql_packet_create(packet_size, conn->sequence_id);
        if (!packet) return -1;
        
        size_t offset = 0;
        
        // OK header
        mysql_packet_write_uint8(packet, offset++, MYSQL_TYPE_OK);
        
        // Affected rows
        offset += mysql_packet_write_lenenc_int(packet, offset, 0);
        
        // Last insert ID
        offset += mysql_packet_write_lenenc_int(packet, offset, 0);
        
        // Status flags
        mysql_packet_write_uint16(packet, offset, conn->status_flags);
        offset += 2;
        
        // Warnings
        mysql_packet_write_uint16(packet, offset, 0);
        offset += 2;
        
        // Info message
        mysql_packet_write_lenenc_string(packet, offset, message);
        
        int result = mysql_send_packet(conn, packet);
        mysql_packet_destroy(packet);
        
        return result;
    } else {
        return mysql_send_ok(conn, 0, 0);
    }
}

int mysql_send_single_value_result(mysql_connection_t* conn, const char* column_name, 
                                  const char* value, mysql_field_type_t type) {
    if (!conn || !column_name) return -1;
    
    mysql_result_set_t* result = mysql_result_set_create(1);
    if (!result) return -1;
    
    // Add field definition
    uint32_t length = value ? strlen(value) : 0;
    mysql_result_set_add_field(result, 0, column_name, type, length);
    
    // Add row
    char* row_data[1] = {(char*)value};
    uint64_t lengths[1] = {length};
    mysql_result_set_add_row(result, row_data, lengths);
    
    int ret = mysql_send_result_set(conn, result);
    mysql_result_set_destroy(result);
    
    return ret;
}